class Vue {
  constructor (options) {
    this.$options = options; //接收用户传来的参数
    this.$data = options.data;
    // 1、对data进行响应式处理
    observe (this.$data);
    // 1.5、设置代理
    proxy (this);
    // 2.模板编译
    new Compile (options.el, this);
  }
}

/**
 * 将传入的值进行响应式处理
 * @param {*} value 
 */
function observe (value) {
  if (Array.isArray (value)) {
    // TODO数组的响应式处理
  } else {
    // 对象的响应式处理
    Object.keys (value).forEach (key => {
      defineReactive (value, key, value[key]);
    });
  }
}

/**
 * 利用defineReactive,将对象的属性进行拦截，也就是响应式处理 
 * @param {*} obj 
 * @param {*} key 
 * @param {*} val 
 */
function defineReactive (obj, key, val) {
  const dep = new Dep ();
  Object.defineProperty (obj, key, {
    get () {
      console.log ('get', key);
      Dep.target && dep.addDep (Dep.target);
      return val;
    },
    set (newVal) {
      if (newVal !== val) {
        console.log ('set', key);

        val = newVal;
        dep.notify ();
      }
    },
  });
}

/**
 * 设置代理
 */
function proxy (vm) {
  Object.keys (vm.$data).forEach (key => {
    Object.defineProperty (vm, key, {
      get () {
        return vm.$data[key];
      },
      set (newVal) {
        if (newVal !== vm.$data[key]) {
          vm.$data[key] = newVal;
        }
      },
    });
  });
}

/**
 * 模板编译
 */
class Compile {
  constructor (el, vm) {
    this.$el = document.querySelector (el);
    this.$vm = vm;
    this.compile (this.$el);
  }
  compile (node) {
    node.childNodes.forEach (node => {
      if (this.isInter (node)) {
        this.update (node);
      }
    });
  }
  isInter (node) {
    return node.nodeType === 3 && /\{\{(.*)\}\}/.test (node.textContent);
  }
  // node {{counter}}
  update (node) {
    const updater = () => {
      node.textContent = this.$vm[RegExp.$1];
    };
    updater ();
    new Watcher (updater, this.$vm, RegExp.$1);
  }
}

/**
 * 响应式更新
 */
class Dep {
  constructor () {
    this.watchers = [];
  }
  addDep (watcher) {
    this.watchers.push (watcher);
  }
  notify () {
    this.watchers.forEach (v => {
      v.update ();
    });
  }
}

class Watcher {
  constructor (updateFn, vm, key) {
    this.$updateFn = updateFn;
    this.$vm = vm;
    this.$key = key;

    Dep.target = this;
    this.$vm[this.$key];
    Dep.target = null;
  }
  update () {
    this.$updateFn ();
  }
}
